﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace WpfClient.Command
{
    public sealed class MvvmCommand : ICommand
    {
        private Action<object> execute = null;

        private Func<object, bool> canExecute = null;

        private GenericWeakEventManager<PropertyChangedEventArgs> weakEventListener;

        private void RequeryCanExecute(object sender, PropertyChangedEventArgs args)
        {
            OnCanExecuteChanged();
        }

        private string GetPropertyName<TEntity>(Expression<Func<TEntity, object>> expression)
        {
            var lambda = expression as LambdaExpression;
            MemberExpression memberExpression;
            if (lambda.Body is UnaryExpression)
            {
                var unary = lambda.Body as UnaryExpression;
                memberExpression = unary.Operand as MemberExpression;
            }
            else
            {
                memberExpression = lambda.Body as MemberExpression;
            }

            var constantExpression = memberExpression.Expression as ConstantExpression;
            var propertyInfo = memberExpression.Member as PropertyInfo;

            return propertyInfo.Name;
        }

        public MvvmCommand(Action<object> execute, Func<object, bool> canExecute)
        {
            this.execute = execute;
            this.canExecute = canExecute;

            weakEventListener =
            new GenericWeakEventManager<PropertyChangedEventArgs>(this.RequeryCanExecute);
        }

        public MvvmCommand(Action<object> execute, Func<object, bool> canExecute,
            INotifyPropertyChanged source, String propertyName)
            : this(execute, canExecute)
        {
            this.execute = execute;
            this.canExecute = canExecute;

            PropertyChangedEventManager.AddListener(source, this.weakEventListener, propertyName);
        }

        public MvvmCommand AddListener<TEntity>(INotifyPropertyChanged source,
            Expression<Func<TEntity, object>> property)
        {
            string propertyName = GetPropertyName(property);
            PropertyChangedEventManager.AddListener(source, weakEventListener, propertyName);
            return this;
        }

        public void OnCanExecuteChanged()
        {
            EventHandler handler = CanExecuteChanged;

            if (handler != null)
            {
                CanExecuteChanged(this, EventArgs.Empty);
            }
        }

        #region ICommand Interface

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            if (this.canExecute == null)
            {
                return true;
            }

            return this.canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            if (this.execute != null)
            {
                execute(parameter);
            }
        }

        #endregion

    }
}
